asp.net 알 리 페 이 제때 장부 인터페이스 사용 상세 설명
그 전에 c\#버 전의 적시에 도착 코드 를 다운로드 해 주세요. 여 기 는 다운로드 주소http://dev.alipay.com/devclub/mvnforum/viewthread_thread,4;jsessionid=595DB7442AAA5CD2FC849E7C2FBE51D7
일단 프로그램의 구성 을 살 펴 보도 록 하 겠 습 니 다.
알 리 페 이 라 는 파일 이 있 습 니 다. AliPay 는 구조 URL 을 포함 한 암호 화 알고리즘 과 같은 것들 입 니 다.
그 럴 까요?이런 종류의 모든 내용
using System.Web;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Net;
using System;
/// <summary>
/// New Interface for AliPay
/// </summary>
namespace Gateway
{
public class AliPay
{
/// <summary>
/// ASP MD5
/// </summary>
public static string GetMD5(string s, string _input_charset)
{
MD5 md5 = new MD5CryptoServiceProvider();
byte[] t = md5.ComputeHash(Encoding.GetEncoding(_input_charset).GetBytes(s));
StringBuilder sb = new StringBuilder(32);
for (int i = 0; i < t.Length; i++)
{
sb.Append(t[i].ToString("x").PadLeft(2, '0'));
}
return sb.ToString();
}
/// <summary>
///
/// a z
/// </summary>
public static string[] BubbleSort(string[] r)
{
int i, j; //
string temp;
bool exchange;
for (i = 0; i < r.Length; i++) // R.Length-1
{
exchange = false; // ,
for (j = r.Length - 2; j >= i; j--)
{//
if (System.String.CompareOrdinal(r[j + 1], r[j]) < 0)
{
temp = r[j + 1];
r[j + 1] = r[j];
r[j] = temp;
exchange = true; // ,
}
}
if (!exchange) // ,
{
break;
}
}
return r;
}
/// <summary>
/// URL
/// </summary>
/// <param name="para"> </param>
/// <param name="_input_charset"> </param>
/// <param name="sign_type"> </param>
/// <param name="key"> </param>
/// <returns> URL </returns>
public static string CreatUrl(
//string gateway,//GET
string[] para,
string _input_charset,
string sign_type,
string key
)
{
int i;
// ;
string[] Sortedstr = BubbleSort(para);
// md5 ;
StringBuilder prestr = new StringBuilder();
for (i = 0; i < Sortedstr.Length; i++)
{
if (i == Sortedstr.Length - 1)
{
prestr.Append(Sortedstr[i]);
}
else
{
prestr.Append(Sortedstr[i] + "&");
}
}
prestr.Append(key);
// Md5 ;
string sign = GetMD5(prestr.ToString(), _input_charset);
// POST
return sign;
// GET
// Url;
// char[] delimiterChars = { '='};
// StringBuilder parameter = new StringBuilder();
// parameter.Append(gateway);
// for (i = 0; i < Sortedstr.Length; i++)
// {//UTF-8
// parameter.Append(Sortedstr[i].Split(delimiterChars)[0] + "=" + HttpUtility.UrlEncode(Sortedstr[i].Split(delimiterChars)[1]) + "&");
// }
//
// parameter.Append("sign=" + sign + "&sign_type=" + sign_type);
//
// // Url;
// return parameter.ToString();
}
// ATN ,
public static string Get_Http(string a_strUrl, int timeout)
{
string strResult;
try
{
HttpWebRequest myReq = (HttpWebRequest)HttpWebRequest.Create(a_strUrl);
myReq.Timeout = timeout;
HttpWebResponse HttpWResp = (HttpWebResponse)myReq.GetResponse();
Stream myStream = HttpWResp.GetResponseStream();
StreamReader sr = new StreamReader(myStream, Encoding.Default);
StringBuilder strBuilder = new StringBuilder();
while (-1 != sr.Peek())
{
strBuilder.Append(sr.ReadLine());
}
strResult = strBuilder.ToString();
}
catch (Exception exp)
{
strResult = " :" + exp.Message;
}
return strResult;
}
}
}
을 보 세 요.우 리 는 보통 이런 종 류 를 상관 하지 않 아 도 됩 니 다.호출 할 수 있 는 것 만 보장 하면 됩 니 다.신경 쓰 지 마 세 요.우리 가 해 야 할 일 은 매우 적다.그 는 어떻게 일 하 는 것 일 까?
개발 문서 의 작업 도 입 니 다.
사실 우리 가 처리 해 야 할 것 은 세 개의 Aspx 파일 뿐 입 니 다.
첫 번 째 부터 볼 게 요. Default.aspx
이것 은 요청 한 인터페이스
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Gateway;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void BtnAlipay_Click(object sender, EventArgs e)
{
// ;
string gateway = "https://www.alipay.com/cooperate/gateway.do?"; //
string service = "create_direct_pay_by_user"; // , ,
string seller_email = " "; // ,
string sign_type = "MD5"; // , “ ”
string key = " "; // , partner , : www.alipay.com, 。
string partner = " "; // ID, ID, ID
string _input_charset = "utf-8"; // , , 。 MD5 。
string show_url = "http://www.alipay.com/"; // , , “ ” 。
string out_trade_no = TxtOrderno.Text.Trim(); // ,
string subject = "4.0 "; // , , ,
string body = " :" + TxtOrderno.Text.Trim() + " :" + TxtTotal_fee.Text.Trim() + " "; // ,
string total_fee = TxtTotal_fee.Text.Trim(); // ,
// url(Alipay_Notify.aspx ),
string notify_url = http://0.0.6.108/Alipay/Alipay_Notify.aspx;
// url(Alipay_Return.aspx ),
string return_url = http://0.0.6.108/Alipay/Alipay_Return.aspx;
// ;
// , , ,
string[] para ={
"service="+service,
"partner=" + partner,
"seller_email=" + seller_email,
"out_trade_no=" + out_trade_no,
"subject=" + subject,
"body=" + body,
"total_fee=" + total_fee,
"show_url=" + show_url,
"payment_type=1",
"notify_url=" + notify_url,
"return_url=" + return_url,
"_input_charset="+_input_charset
};
// URL
string aliay_url = AliPay.CreatUrl(
//gateway,//GET
para,
_input_charset,
sign_type,
key
);
// GET
//Response.Redirect(aliay_url);
// POST
Response.Write("<form name='alipaysubmit' method='post' action='https://www.alipay.com/cooperate/gateway.do?_input_charset=utf-8'>");
Response.Write("<input type='hidden' name='service' value=" + service + ">");
Response.Write("<input type='hidden' name='partner' value=" + partner + ">");
Response.Write("<input type='hidden' name='seller_email' value=" + seller_email + ">");
Response.Write("<input type='hidden' name='out_trade_no' value=" + out_trade_no + ">");
Response.Write("<input type='hidden' name='subject' value=" + subject + ">");
Response.Write("<input type='hidden' name='body' value=" + body + ">");
Response.Write("<input type='hidden' name='total_fee' value=" + total_fee + ">");
Response.Write("<input type='hidden' name='show_url' value=" + show_url + ">");
Response.Write("<input type='hidden' name='return_url' value=" + return_url + ">");
Response.Write("<input type='hidden' name='notify_url' value=" + notify_url + ">");
Response.Write("<input type='hidden' name='payment_type' value=1>");
Response.Write("<input type='hidden' name='sign' value=" + aliay_url + ">");
Response.Write("<input type='hidden' name='sign_type' value=" + sign_type + ">");
Response.Write("</form>");
Response.Write("<script>");
Response.Write("document.alipaysubmit.submit()");
Response.Write("</script>");
}
}
코드 에 있 는 key 와 partner 의 획득 방법이제 우 리 는 코드 에 있 는 정보 에 따라 똑 같이 작성 하면 된다.
그리고 원행 홈 페이지.
점 제출
들 어 갑 니 다.
아래 의 조작 은 바로 지불 플랫폼 의 완성 으로 우리 의 절차 와 관계 가 없다.
지금 우 리 는 생각해 야 한다.그럼 돈 을 지불 한 후에?
저희 가 두 개의 인터페이스 가 있 는데 하 나 는...
Alipay_Return.aspx
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Text;
using System.Collections.Specialized;
using System.IO;
using Gateway;
/// <summary>
/// , , , 。
/// “ ”, , , “ , ”。
/// </summary>
public partial class Alipay_Return : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string alipayNotifyURL = "https://www.alipay.com/cooperate/gateway.do?service=notify_verify";
//string alipayNotifyURL = "http://notify.alipay.com/trade/notify_query.do?";// 。
string key = " "; //partner ( )
string partner = " "; //partner id( )
string _input_charset = "utf-8";// , , 。 MD5 。
alipayNotifyURL = alipayNotifyURL + "&partner=" + partner + "¬ify_id=" + Request.QueryString["notify_id"];
// ATN ,true ,false
string responseTxt = AliPay.Get_Http(alipayNotifyURL, 120000);
//******* //*******
int i;
NameValueCollection coll;
//Load Form variables into NameValueCollection variable.
coll = Request.QueryString;
// Get names of all forms into a string array.
String[] requestarr = coll.AllKeys;
// ;
string[] Sortedstr = AliPay.BubbleSort(requestarr);
// md5 ;
StringBuilder prestr = new StringBuilder();
for (i = 0; i < Sortedstr.Length; i++)
{
if (Request.Form[Sortedstr[i]] != "" && Sortedstr[i] != "sign" && Sortedstr[i] != "sign_type")
{
if (i == Sortedstr.Length - 1)
{
prestr.Append(Sortedstr[i] + "=" + Request.QueryString[Sortedstr[i]]);
}
else
{
prestr.Append(Sortedstr[i] + "=" + Request.QueryString[Sortedstr[i]] + "&");
}
}
}
prestr.Append(key);
// Md5 ;
string mysign = AliPay.GetMD5(prestr.ToString(), _input_charset);
//******* *******
string sign = Request.QueryString["sign"];
// Response.Write(prestr.ToString()); // , 。
if (mysign == sign && responseTxt == "true") // ,
{
// ,
string strOrderNO = Request.QueryString["out_trade_no"];//
string strPrice = Request.QueryString["total_fee"];//
string strTradeStatus = Request.QueryString["TRADE_STATUS"];//
//
//OfficeFinanceServices objOfficeFinanceServices = new OfficeFinanceServices();
//
string[] login = strOrderNO.Split(new string[] { "_" }, StringSplitOptions.RemoveEmptyEntries);
////
//decimal objdm = objOfficeFinanceServices.OfficeFinanceSelect(Convert.ToInt32(login[0].ToString().Trim()), true);
//OfficeFinance objofficeFinance = new OfficeFinance();
//objofficeFinance.ofId = Convert.ToInt32(login[0].ToString().Trim());
//objofficeFinance.ofOrid = "cz";
//objofficeFinance.ofTime = DateTime.Now;
//objofficeFinance.ofType = 1;
//objofficeFinance.ofAmount = Convert.ToDecimal(strPrice.ToString().Trim());
//objofficeFinance.ofRemainAmount = Convert.ToDecimal(strPrice.ToString().Trim()) + objdm;
//objofficeFinance.ofIsCurrentValue = 1;
//objofficeFinance.ofUserId = -10;
//objofficeFinance.ofNote1 = " :" + strPrice.ToString().Trim() + " ";
//objofficeFinance.ofNote2 = " :" + login[1].ToString().Trim() + " :" + strPrice + " ";
//objOfficeFinanceServices.addOfficeFinanceOne(objofficeFinance);
Response.Write(" :" + login[1].ToString().Trim() + "<br> :" + strPrice + " "); // , ,
//// , , md5 ( txt , )
string TOEXCELLR = "MD5 :mysign=" + mysign + ",sign=" + sign + ",responseTxt=" + responseTxt + " " + " :" + login[1].ToString().Trim() + "<br> :" + strPrice + " ";
StreamWriter fs = new StreamWriter(Server.MapPath("Return_DATA/" + DateTime.Now.ToString().Replace(":", "")) + ".txt", false, System.Text.Encoding.Default);
fs.Write(TOEXCELLR);
fs.Close();
}
else
{
Response.Write("------------------------------------------");
Response.Write("<br>Result:responseTxt=" + responseTxt);
Response.Write("<br>Result:mysign=" + mysign);
Response.Write("<br>Result:sign=" + sign);
Response.Write(" ");
//// , , md5 ( txt , )
string TOEXCELLR = "MD5 :mysign=" + mysign + ",sign=" + sign + ",responseTxt=" + responseTxt;
StreamWriter fs = new StreamWriter(Server.MapPath("Return_DATA/" + DateTime.Now.ToString().Replace(":", "")) + ".txt", false, System.Text.Encoding.Default);
fs.Write(TOEXCELLR);
fs.Close();
// ,
}
}
}
이것 은 거래 가 성공 한 후에 호출 할 인터페이스 입 니 다.여기 서 조금 만 바 꾸 면 됩 니 다.사실 우리 가 해 야 할 일 은 여 기 를 바 꾸 는 것 입 니 다.여기 서 데이터 베 이 스 를 업데이트 하면 됩 니 다.코드
//
//OfficeFinanceServices objOfficeFinanceServices = new OfficeFinanceServices();
//
string[] login = strOrderNO.Split(new string[] { "_" }, StringSplitOptions.RemoveEmptyEntries);
////
//decimal objdm = objOfficeFinanceServices.OfficeFinanceSelect(Convert.ToInt32(login[0].ToString().Trim()), true);
//OfficeFinance objofficeFinance = new OfficeFinance();
//objofficeFinance.ofId = Convert.ToInt32(login[0].ToString().Trim());
//objofficeFinance.ofOrid = "cz";
//objofficeFinance.ofTime = DateTime.Now;
//objofficeFinance.ofType = 1;
//objofficeFinance.ofAmount = Convert.ToDecimal(strPrice.ToString().Trim());
//objofficeFinance.ofRemainAmount = Convert.ToDecimal(strPrice.ToString().Trim()) + objdm;
//objofficeFinance.ofIsCurrentValue = 1;
//objofficeFinance.ofUserId = -10;
//objofficeFinance.ofNote1 = " :" + strPrice.ToString().Trim() + " ";
//objofficeFinance.ofNote2 = " :" + login[1].ToString().Trim() + " :" + strPrice + " ";
//objOfficeFinanceServices.addOfficeFinanceOne(objofficeFinance);
여러분 은 지금 저 에 게 왜 그들 을 주석 하 셨 는 지 물 어보 실 것 입 니까?제 가 테스트 를 해 봤 는데 이 화면 은 성공 할 때 한 번 만 호출 한 후에 다시 호출 하지 않 습 니 다.그래서 이런 좋 지 않 은 점 이 있 습 니 다.바로 일부 고객 들 이 계좌 이체 에 대해 베테랑 입 니 다.거래 가 성공 한 후에 이 화면 을 호출 하지 않 고 탐색 기 를 닫 았 습 니 다.이때 어떤 문제 가 발생 할 까요?그럼 알 리 페 이 는 다 시 는 이 화면 을 사용 하지 않 을 것 입 니 다.다음 거래 를 기다 릴 수 밖 에 없습니다.돈 은 전 화 했 습 니 다.저희 도 받 았 습 니 다.그런데 고객 에 게 돈 을 충전 하지 않 았 습 니 다.이때 고객 이 회사 에 전 화 를 했 습 니 다.어떻게 된 거 예요?돈 이 전 화 했 습 니 다.그런데 제 쪽 에 아직 입금 되 지 않 았 습 니 다.알 리 페 이에 올 라 가 보 니 있 는데 입금 되 지 않 았 습 니 다.어떻게 해 야 합 니까?마지막 으로 고객 에 게 수 동 으로 올 릴 수 밖 에 없 었 지만 프로그램 이 자동 으로 올 라 갈 까 봐 걱정 이 되 었 습 니 다.ㅎ 그래서 여기 서 이 페이지 를 사용 하 는 것 을 권장 하지 않 습 니 다.만약 당신 이 자신의 주문 상 태 를 수정 하 는 것 이 라면 당연히 가능 합 니 다.잔액 은 여기에 쓰 지 말고 이 Alipay 라 고 쓰 세 요.Notify.aspx 페이지 에서 이 화면 은 거래 상태 가 바 뀌 었 을 때 호출 되 고 성공 적 인 소식 을 듣 기 전에 계속 호출 됩 니 다.시효 가 24 시간 입 니 다.그러면 우 리 는 그 가 성공 페이지 로 넘 어 갔 는 지 신경 쓰 지 않 아 도 됩 니 다.이 페이지 를 인터넷 에 걸 고 통 지 를 기다 리 면 됩 니 다.거래 가 성 공 했 을 때 입금 하면 됩 니 다.코드
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Text;
using System.Collections.Specialized;
using System.IO;
using Gateway;
using SystemModel;
/// <summary>
/// , HTML 。
/// “ ”, 。
/// , ,
/// </summary>
public partial class Alipay_Notify : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string alipayNotifyURL = "https://www.alipay.com/cooperate/gateway.do?service=notify_verify";
//string alipayNotifyURL = "http://notify.alipay.com/trade/notify_query.do?";// 。
string partner = " "; //partner id( )
string key = " "; //partner ( )
string _input_charset = "utf-8";// , , 。 MD5 。
alipayNotifyURL = alipayNotifyURL + "&partner=" + partner + "¬ify_id=" + Request.Form["notify_id"];
// ATN ,true ,false
string responseTxt = AliPay.Get_Http(alipayNotifyURL, 120000);
//******* *******
int i;
NameValueCollection coll;
//Load Form variables into NameValueCollection variable.
coll = Request.Form;
// Get names of all forms into a string array.
String[] requestarr = coll.AllKeys;
// ;
string[] Sortedstr = AliPay.BubbleSort(requestarr);
// md5 ;
StringBuilder prestr = new StringBuilder();
for (i = 0; i < Sortedstr.Length; i++)
{
if (Request.Form[Sortedstr[i]] != "" && Sortedstr[i] != "sign" && Sortedstr[i] != "sign_type")
{
if (i == Sortedstr.Length - 1)
{
prestr.Append(Sortedstr[i] + "=" + Request.Form[Sortedstr[i]]);
}
else
{
prestr.Append(Sortedstr[i] + "=" + Request.Form[Sortedstr[i]] + "&");
}
}
}
prestr.Append(key);
string mysign = AliPay.GetMD5(prestr.ToString(), _input_charset);
//******* *******
string sign = Request.Form["sign"];
if (mysign == sign && responseTxt == "true") // , , ,
// success , ,
{
if (Request.Form["trade_status"] == "WAIT_BUYER_PAY")// _ ( )
{
// ,
string strOrderNO = Request.Form["out_trade_no"];//
string strPrice = Request.Form["total_fee"];// , , 。
string TOEXCELL = "MD5 :mysign=" + mysign + ",sign=" + sign + ",responseTxt=" + responseTxt;
StreamWriter f = new StreamWriter(Server.MapPath("Notify_DATA/" + DateTime.Now.ToString().Replace(":", "")) + ".txt", false, System.Text.Encoding.Default);
f.Write(TOEXCELL + " :" + strOrderNO.ToString().Trim() + " :" + strPrice.ToString().Trim());
f.Close();
Alipaym objalipay = new Alipaym();
objalipay.APID = strOrderNO;
objalipay.addTime = DateTime.Now;
objalipay.total_fee = Convert.ToDecimal(strPrice);
objalipay.trade_status = " ";
objalipay.Text1 = DateTime.Now.ToString();
objalipay.Text2 = "";
objalipay.Text3 = "";
objalipay.Text4 = "";
objalipay.Text5 = "";
OfficeFinanceServices.Update(objalipay);
}
else if (Request.Form["trade_status"] == "TRADE_FINISHED" || Request.Form["trade_status"] == "TRADE_SUCCESS")// _ ( )
{
// ,
string strOrderNO = Request.Form["out_trade_no"];//
string strPrice = Request.Form["total_fee"];//
//
OfficeFinanceServices objOfficeFinanceServices = new OfficeFinanceServices();
//
string[] login = strOrderNO.Split(new string[] { "_" }, StringSplitOptions.RemoveEmptyEntries);
////
decimal objdm = objOfficeFinanceServices.OfficeFinanceSelect(Convert.ToInt32(login[0].ToString().Trim()), true);
OfficeFinance objofficeFinance = new OfficeFinance();
objofficeFinance.ofId = Convert.ToInt32(login[0].ToString().Trim());
objofficeFinance.ofOrid = "cz";
objofficeFinance.ofTime = DateTime.Now;
objofficeFinance.ofType = 1;
objofficeFinance.ofAmount = Convert.ToDecimal(strPrice.ToString().Trim());
objofficeFinance.ofRemainAmount = Convert.ToDecimal(strPrice.ToString().Trim()) + objdm;
objofficeFinance.ofIsCurrentValue = 1;
objofficeFinance.ofUserId = -10;
objofficeFinance.ofNote1 = " :" + strPrice.ToString().Trim() + " ";
objofficeFinance.ofNote2 = " :" + login[1].ToString().Trim() + " :" + strPrice + " ";
if (objOfficeFinanceServices.addOfficeFinanceOne(objofficeFinance))
{
Response.Write("success");
Alipaym objalipay = new Alipaym();
objalipay.APID = strOrderNO;
objalipay.addTime = DateTime.Now;
objalipay.total_fee = Convert.ToDecimal(strPrice);
objalipay.trade_status = " ";
objalipay.Text1 = DateTime.Now.ToString();
objalipay.Text2 = "";
objalipay.Text3 = "";
objalipay.Text4 = "";
objalipay.Text5 = "";
OfficeFinanceServices.Update(objalipay);
}
else
{
Response.Write("fail");
}
}
else
{
// ,
}
//Response.Write("success"); // , , success
//success fail , success , ( , ),
// success, , , 24 。
//// , , md5 ( txt , )
string TOEXCELLR = "MD5 :mysign=" + mysign + ",sign=" + sign + ",responseTxt=" + responseTxt;
StreamWriter fs = new StreamWriter(Server.MapPath("Notify_DATA/" + DateTime.Now.ToString().Replace(":", "")) + ".txt", false, System.Text.Encoding.Default);
fs.Write(TOEXCELLR);
fs.Close();
}
else
{
Response.Write("fail");
// , , md5 ( txt , )
string TOEXCELLR = "MD5 :mysign=" + mysign + ",sign=" + sign + ",responseTxt=" + responseTxt;
StreamWriter fs = new StreamWriter(Server.MapPath("Notify_DATA/" + DateTime.Now.ToString().Replace(":", "")) + ".txt", false, System.Text.Encoding.Default);
fs.Write(TOEXCELLR);
fs.Close();
}
}
}
는 로그 파일 을 기록 하 는 것 이 좋 습 니 다.그런데 문제 가 생 겼 을 때 참고 할 만 한 부분 이 있 습 니 다.제 코드 가 좀 어 지 럽 습 니 다.여러분 들 이 적당 하 게 고 쳐 주 셔 도 됩 니 다.ㅎ ㅎsufei1013
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
작업 중 문제 해결 - (win 2003 asp. net) Session 과 페이지 전송 방법 으로 해결 방안 을 정상적으로 사용 할 수 없습니다.또한 F 는 처음에 우리 의 BP & IT 프로젝트 팀 이 Forms 폼 검증 을 사용 했다 고 판단 할 수 있 습 니 다. 페이지 를 뛰 어 넘 는 것 은http://hr.bingjun.cc/MyTask/MyTas...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.