2014-12-06 2 views
0

Это очень запутанная проблема, поэтому я сделаю все возможное, чтобы ее разработать. У меня есть приложение facebook холста, который принимает платежи и когда пользователь нажимает кнопку оплаты, происходит следующее:Функции обратного вызова в неправильном порядке

  1. Моей Javascript функции обратного вызова вызывается и передается платежный идентификатор, и поэтому я сохранить этот платежный идентификатор и другой info в мою базу данных заказов.

  2. Facebook вызывает URL-адрес обратного вызова, который у меня настроен, чтобы сообщить мне, когда оплата будет проходить. Он дает мне только идентификатор платежа, поэтому я использую его для поиска в базе данных для строки с идентификатором платежа, который они отправили. Затем я использую информацию в строке, чтобы заполнить электронное письмо, которое я отправляю клиенту для подтверждения заказа.

Моя большая проблема в том, что для какой-то причине шаг 2 становится закончена до выполнения шага 1 так, когда я пытаюсь посмотреть платежный идентификатор в базе данных, пока не существует, так что я не могу отправить электронная почта к клиенту. Что я могу сделать, чтобы исправить это? У меня есть псевдо-код ниже для обоих шагов.

Шаг 1:

using (OrderDBContext order = new OrderDBContext()) 
      { 
       string message = Encryption.SimpleDecryptWithPassword(orderDetails.request_id, GlobalFacebookConfiguration.Configuration.AppId, 0); 
       string[] finalMessage = message.Split('@'); 
       int orderID = Convert.ToInt16(finalMessage.ElementAtOrDefault(2)); 
       Models.Order row = order.Orders.Where(i => i.ID == orderID).FirstOrDefault(); 

       switch (orderDetails.status) 
       { 
        case "completed": 
         row.PaymentID = orderDetails.payment_id; 
         row.Currency = orderDetails.currency; 
         row.HashKey = orderDetails.request_id; 
         row.Paid = true; 

         order.SaveChanges(); 
         return Json(new { message = "Your payment was processed! You will receive a confirmation email soon." }, JsonRequestBehavior.AllowGet); 
        case "initiated": 
         row.PaymentID = orderDetails.payment_id; 
         row.Currency = orderDetails.currency; 
         row.HashKey = orderDetails.request_id; 
         row.Paid = false; 

         order.SaveChanges(); 
         return Json(new { message = "Your payment is being processed! You will receive a confirmation email as soon as the payment is confirmed." }, JsonRequestBehavior.AllowGet); 
       } 
      } 

Шаг 2:

dynamic result = new StreamReader(request.InputStream).ReadToEnd(); 
       var items = JsonConvert.DeserializeObject<RootObject>(result); 
       string paymentID; 

       if (items.entry != null && items.entry.Count > 0) 
       { 
        paymentID = items.entry[0].id; 
       } 
       else 
       { 
        // logic when items.entry is null or doesn't have any elements 
        paymentID = null; 
       } 

       if (PaymentHelper.confirmPayment(paymentID, GlobalFacebookConfiguration.Configuration.AppId, GlobalFacebookConfiguration.Configuration.AppSecret)) 
       { 
        // if payment is confirmed then send email to us with the order details 
        // then send confirmation email to user letting them know that we are working on it 
        using (OrderDBContext order = new OrderDBContext()) 
        { 
         Order row = order.Orders.Where(i => i.PaymentID == paymentID).FirstOrDefault(); 
         SendEmail.sendOrderDetailsToWriter(row); 
         SendEmail.sendOrderDetailsToCustomer(row); 
        } 
       } 
+0

вам потребуется 3-й шаг, синхронизирующий 2 первых. – tschmit007

+0

@ tschmit007 не уверен, что вы имеете в виду именно. можете ли вы привести примерный код? – user3610374

ответ

1

Что инициирует операцию Facebook? Что-то в шаге № 1 заставляет шаг №2 запускаться? Или оба этапа начинаются асинхронно и вместе?

Предполагая, что последний (так как это более трудный сценарий), вы должны сделать что-то вроде этого:

readonly object o = new object(); 
bool databaseUpdated; 

// user clicked the payment button 
void onClick() 
{ 
    databaseUpdated = false; 

    StartStep1(); 
    StartStep2(); 
} 

// completion routines for steps #1 and #2 
void stepOneDone() 
{ 
    lock (o) 
    { 
     // do the database update here...i.e. the code you posted for step #1 

     databaseUpdated = true; 
     Monitor.Pulse(o); 
    } 
} 

void stepTwoDone() 
{ 
    lock (o) 
    { 
     while (!databaseUpdated) 
     { 
      Monitor.Wait(o); 
     } 

     // Process Facebook response here...i.e. the code you posted for step #2 
    } 
} 

выше использует общий замок для двух операций синхронизации друг с другом. Флаг databaseUpdated указывает, конечно, завершено ли обновление базы данных. Если завершение этапа №2 инициируется до того, как обновление базы данных даже удалось запустить (например, шаг 2 получает блокировку до того, как шаг № 1 может), он проверит флаг, заметьте, что он еще не установлен и будет ждать. Вызов Monitor.Wait() освобождает блокировку, чтобы шаг №1 мог ее принять. Затем шаг № 1 делает то, что ему нужно сделать, устанавливает флаг и сигнализирует о том, что он может продолжить.

Конечно, если шаг № 1 сначала закроет замок, шаг № 2 даже не сможет его получить. К тому моменту, когда блокировка будет доступна снова, а шаг №2 может пройти мимо инструкции lock, флаг будет установлен, и он может пойти своим весельем. :)

Возможно, есть интересный способ решить проблему с использованием новой идиомы async/await, но без дополнительного контекста я не могу сказать. Вышеприведенное должно работать точно.

И, наконец, незначительный nit: почему вы объявляете шаг # 2 result переменным как dynamic? Метод ReadToEnd() никогда не вернет ничего, кроме string.Использование dynamic здесь в лучшем случае бессмысленно и потенциально дополнительные накладные расходы в худшем случае из-за требуемого динамического привязки (в зависимости от того, компилятор C# замечает, что это бессмысленно и hellip; я не помню, с какими правилами компиляции там).

+0

Пользователь инициирует шаги, нажав кнопку оплаты, и шаг 1 начинается сразу же после закрытия окна оплаты, но шаг 2, по сути, зависит от Facebook, поскольку они начинают отправку сообщения в ответ, когда платеж завершается с их конца. В моей локальной отладке шаг 1 всегда проходит сначала, но когда я просматриваю facebook напрямую, чтобы посмотреть, как работает приложение, шаг 2 всегда проходит первым. Надеюсь, это даст вам необходимый вам контекст. – user3610374

+0

Вы пытались применить технику, описанную выше? Если нет, вы должны. Если вы это сделали, это сработало? Если нет, вы должны подробно описать, почему бы и нет. –

+0

У меня еще нет. Я собираюсь попробовать это сейчас. Я только хотел сначала ответить на ваши вопросы. – user3610374

1

в псевдокоде:

Semaphore = 0; 

function Step1() { 
    //do your stuff 
    Semaphore++; 
} 

function Step2() { 
    //do your stuff 
    Semaphore++; 
} 

function Step3() { 
    while (1) { // for example a setTimer in javascript 
     if (Semaphore >= 2) { 
      // send email 
      break; 
     } 
     if (timeout()) { 
      // send error message 
      break; 
     } 
     sleep(200); 
    } 
} 

Step1.Start(); 
Step2.Start(); 
Step3.Start(); 

Вы можете, конечно, найти яваскрипт структуру, позволяющую синхронизировать две задачи. Здесь это действительно простая и наивная синхронизация.