나는 asp.net 페이지에서 신용 카드 정보와 결제 금액을 입력하여 결제 권한을 부여 할 수 있습니다. 약 2 주 전 갑자기 우리는 이중 청구에 대한보고를 받기 시작했지만 페이지를 변경하지 않았습니다. 클릭 할 때 제출 버튼을 사용 중지하도록 페이지가 이미 설정되었습니다. 이 문제를 해결하기 위해 버튼을 클릭 할 때 플래그를 설정 했으므로 플래그가 설정되어 있으면 버튼이 다시 게시 할 수 없습니다 (다른 페이지에서 사용하는 방법입니다). 그다지 문제는 없습니다). 그러나 그것은 계속 발생합니다.중복 된 포스트 백이있는 비활성화 된 버튼이있는 ASP.NET
사용자가 페이지를 새로 고침하여 문제를 일으킬 가능성이 거의없는 것으로 보이는 몇 가지 이유가 있습니다. 먼저 WPF 웹 브라우저 컨트롤에 페이지를 표시하고 창과 일치하며 웹 페이지 일 수도있는 유일한 표시는 포스트 백의 클릭 노이즈, 몸을 마우스 오른쪽 버튼으로 클릭 한 경우 또는있을 경우 페이지 오류. 유일한 새로 고침 또는 뒤로 버튼은 브라우저의 컨텍스트 메뉴에 있습니다. 다음으로 페이지 오류가 발생하지 않는 한 사용자가 새로 고침하거나 되돌아 가려는 동기가 없다고 생각할 수 있지만 오류가없는 것으로보고합니다. 마지막으로, 세션에서 토큰을 배치하고 카드를 처리하기 전에이를 점검하여 서버 측에서 중복 된 포스트 백을 피하기위한 조치를 취했습니다. 따라서 사용자는 첫 번째 요청이 세션 상태에 토큰을 쓸 수있는 것보다 "재시도"버튼을 빠르게 새로 고쳐야합니다. 이를 달성하는 가장 빠른 방법은 보도 자료 제출, F5, 모두 연속으로 입력하는 것입니다. 나는 그것이 일어날 수 있다는 것을 알 수있는 유일한 방법을 무시하는 것을 싫어하지만, 이것이 일어나고있는 것이 아니라고 말하는 것이 안전하다고 보입니다. 마지막으로, 페이지를 게시 할 때 스크립팅 객체를 통해 WPF 응용 프로그램에 신호를 보내면 브라우저가 사라지기 전에 다시 게시 한 후 사용자가 페이지에서 아무 것도 할 수 없도록 닫을 수 있습니다.
유일한 문제는 무엇이 일어나고 있는지 모르겠습니다. 어떻게 든 제출은 자바 스크립트 안전 가드와 서버 측 토큰 안전 가드를 지나쳐 버렸고 이중 과금을 받았고 나는 어떻게 될지 전혀 모른다. 그들은 서로 2 초 이내에 일어난 것으로 기록되었습니다. WPF 앱의 코드가 새로 고침을 호출하거나 브라우저 탐색을 제어하지 않는다는 것을 확인했습니다. 누구든지 아이디어가 있습니까?
<style type="text/css">
...
</style>
<script type="text/javascript" language="javascript">
function OnProcessing(button) //
{
//Check if client side validation passes before disabling
// if postback - return false. If it's 1, then it's a postback.
if (document.getElementById("<%=HFSubmitForm.ClientID %>").value == '1') {
return false;
}
else {
// mark that submit is to be done and return true
document.getElementById("<%=HFSubmitForm.ClientID %>").value = '1';
button.disabled = true;
window.external.OnPaymentProcessing();
return true;
}
}
</script>
</head>
<body id="body" runat="server" style="font-family: arial, Helvetica, sans-serif; font-size: 11px;" scroll="no" onkeydown="return CancelEnterKey(event)">
<form id="form1" runat="server">
<asp:scriptmanager ID="Scriptmanager1" runat="server" EnablePageMethods="True"></asp:scriptmanager>
<script src="Resources/Scripts/CardInput.js?<%= DateTime.Now.Ticks %>" type="text/javascript" language="javascript"></script>
<div id="divCardSwiper" style="text-align:center;" runat="server">
<input id="txtSwipeTarget" type="text" onblur="FocusOnSwipeTarget()" onkeydown="return SwipeTargetCharAdded(event)"
style="position: absolute; left: -1000px" />
<table style="margin-left:auto; margin-right:auto">
<tr>
<td style="text-align:center">
<span style="font-size: 20pt; font-weight: bold; color: #808080">Please Swipe Credit Card</span>
</td>
</tr>
<tr><td style="text-align:center"><img alt="Card Swiper Image" src="Resources/scra-magnesafe-mini-3.png"/></td></tr>
<tr><td style="text-align:center"><span style="font-size: 12pt; font-weight: bold; color: #808080">Or <a href="#" onclick="ManualEntry();return false;">click here</a> to enter manually.</span></td></tr>
</table>
</div>
<div id="divCcForm" runat="server">
<table>
<!-- Input Fields -->
</table>
<asp:Label ID="lblError" runat="server" Font-Bold="True" ForeColor="Red"></asp:Label>
<div style="text-align:center;">
<asp:Button ID="btnProcess" runat="server"
Text="Process" OnClick="btnProcess_Click" OnClientClick="if (OnProcessing(this)==false){return false;}" UseSubmitBehavior="False"/>
<p><strong>Processing may take a moment.<br><font color="red">PLEASE ONLY CLICK PROCESS ONCE</font></strong></p>
</div>
</div>
<asp:Label ID="label1" runat="server" Visible="False"></asp:Label>
<asp:HiddenField ID="HFRequestToken" runat="server"/>
<asp:HiddenField ID="HFSubmitForm" runat="server"/>
</form>
</body>
protected void btnProcess_Click(object sender, EventArgs e)
{
if (IsProcessing())
{
//Payment was already processing
btnProcess.Enabled = false; //Make sure button doesn't become available again
logger.Warn(String.Format("PaymentCollection.aspx was submitted multiple times. Only processing the initial request (Session Token: {0}). FacilityID: {1}, FamilyID: {2}, Amount: {3}",
Session[_postBackTokenKey], ViewState[_facilityIDKey], ViewState[_familyIDKey], txtAmount.Text));
return;
}
lblError.Text = String.Empty;
string script = "window.external.OnPaymentProcessingCancelled()";
bool isRefund = (bool)ViewState[_isRefundKey];
bool processed = false;
if (ValidateForm(isRefund))
{
ProcessingInput pi = new ProcessingInput();
try
{
CreditCardType cardType = (CreditCardType)Int32.Parse(ddlCardType.SelectedValue);
pi.CreditCardNumber = txtCardNum.Text.Trim();
pi.ExpirationMonth = Int32.Parse(ddlExpMo.SelectedValue);
pi.ExpirationYear = Int32.Parse(ddlExpYr.SelectedValue);
pi.FacilityID = new Guid(ViewState[_facilityIDKey].ToString());
pi.FamilyID = new Guid(ViewState[_familyIDKey].ToString());
pi.NameOnCard = txtName.Text.Trim();
pi.OrderID = Guid.NewGuid();
pi.PaymentType = cardType.ToMpsPaymentType();
pi.PurchaseAmount = Math.Abs(Decimal.Parse(txtAmount.Text));
pi.Cvc = txtCvc.Text.Trim();
pi.IsCardPresent = cbCardPresent.Checked;
if (pi.PurchaseAmount >= 0.01m)
{
MerchantProcessingClient svc = new MerchantProcessingClient();
try
{
ProcessingResult result;
logger.Debug("Processing transaction (Session Token: {0}) for Facility: {1}, Family: {2}, Purchase Amount{3}",
Session[_postBackTokenKey], pi.FacilityID, pi.FamilyID, pi.PurchaseAmount);
if (!isRefund)
result = svc.AuthorizePayment(pi);
else
result = svc.RefundTransaction(pi);
if (result.Approved)
{
//Signal Oasis that it can continue
StringBuilder scriptFormat = new StringBuilder();
scriptFormat.AppendLine("window.external.OrderID = '{0}';");
scriptFormat.AppendLine("window.external.AuthCode = '{1}';");
scriptFormat.AppendLine("window.external.AmountCharged = {2};");
scriptFormat.AppendLine("window.external.SetPaymentDateFromBinary('{3}');"); //Had to script Int64 as string or it caused an overflow exception for some reason
scriptFormat.AppendLine("window.external.CcLast4 = '{4}';");
scriptFormat.AppendLine("window.external.SetCreditCardType({5});");
scriptFormat.AppendLine("window.external.CardPresent = {6};");
scriptFormat.AppendLine("window.external.OnPaymentProcessed();");
script = String.Format(scriptFormat.ToString(), result.OrderID, result.AuthCode, result.TransAmount, result.TransDate.ToBinary(),
(result.MaskedCardNum == null ? String.Empty : result.MaskedCardNum.Replace("*", "")), (int)cardType,
pi.IsCardPresent.ToString().ToLower());
processed = true; //Don't allow processing again
}
else
{
//log and display errors
}
}
catch (Exception ex)
{
//log, email, and display errors
}
}
else
lblError.Text = "Transaction Amount is zero or too small to process.";
}
catch (Exception ex)
{
//log, e-mail, and display errors
}
}
this.ClientScript.RegisterStartupScript(this.GetType(), "PaymentApprovedScript", script, true);
//Session[_isProcessingKey] = processed; //Set is processing back to false if there was an error
if (!processed)
Session[_postBackTokenKey] = null; //Clear postback token if there was an error to allow re-submission
}
private bool IsProcessing()
{
bool isProcessing = false;
Guid postbackToken = new Guid(HFRequestToken.Value);
// This won't prevent simultaneous POSTs because the second could read the value from
// session before the first writes it to session. It will help eliminate duplicate posts
// if the user is messing with the back button or refreshing.
if (Session[_postBackTokenKey] != null && (Guid)Session[_postBackTokenKey] == postbackToken)
isProcessing = true;
else
Session[_postBackTokenKey] = postbackToken;
return isProcessing;
}
사용자가 브라우저에서 뒤로를 클릭하고 포스트 백을 다시 제출합니까? –
로마인에게 감사드립니다. 내가 자바 스크립트가 사용자가 완벽하게 시간을 가질 수 있도록 게시 할 때 창을 닫을 수 있다고 WPF 앱에 알리는 것을 잊어 버렸습니다. 백 스페이스 키를 누르면 새로 고침 옵션이 조금 더 나을 가능성이 있지만 발생 및 빈도 (거의 하루에 한 번, 때로는 한 번)의 갑작스런 웨이브와 결합하면 이럴 가능성이 희박해질 수 있습니다. – xr280xr
관련 코드를 볼 수 있습니까? ASPX 마크 업과 모든 자바 스크립트, VB/C# .. –