diff --git a/docs/workshops/day_2_tdd.md b/docs/workshops/day_2_tdd.md
index 9526f8d7da1c93d8dee7ed76c3ead23ccce9740e..65f0a0e2f233f857bfb7f04cdc86505c19b10c28 100644
--- a/docs/workshops/day_2_tdd.md
+++ b/docs/workshops/day_2_tdd.md
@@ -1,9 +1,502 @@
 # Day 2: Test-Driven Development
 
-* Session 1: 9:00 - 10:00: [Testing Category][1] 
-* Session 2: 10:15 - 11:30: Hands-on: A bigger Case study 
-* Session 3: 13:30 - 15:00: Hands-on: A bigger Case study  
-* Session 4: 15:30 - 16:30: Overview, Discussion, Lesson learned  
+* Session 1: 9:00 - 10:00: [Testing Category][1]
+* Session 2: 10:15 - 11:30: Hands-on: A bigger Case study
+* Session 3: 13:30 - 15:00: Hands-on: A bigger Case study
+* Session 4: 15:30 - 16:30: Overview, Discussion, Lesson learned
 
-[1]: https://docs.google.com/presentation/d/1f1vpyYOu3GSyIeyqGLU-D6REBn0ACN81?rtpof=true&usp=drive_fs 
 
+## Dive Deeper into Unit Testing
+
+Pada hari pertama, telah disediakan contoh bagaimana membuat aplikasi web berbasis Spring Boot dengan *unit test* dan functional test suite yang lengkap.
+Pada hari ini, kita akan mendalami terkait bagaimana membuat *unit test suite* yang memenuhi 5 aspek FIRST principle, sembari mengikuti *flow* Test-Driven Development:
+- Fast: proses *testing* secara keseluruhan harus dilakukan dengan cepat.
+- Isolated: setiap *unit test* **tidak boleh memengaruhi hasil** *unit test* lain.
+- Repeatable: hasil tes konsisten meskipun dijalankan berkali-kali pada kondisi yang sama.
+- Self-Validating: bisa digunakan untuk mengecek kesesuaian aplikasi jika terjadi perubahan kode.
+- Thorough: sebisa mungkin mencakup keseluruhan kode dan *business logic* aplikasi.
+
+Untuk *hands-on* ini, kita akan fokus ke sisi back-end dari aplikasi.
+Kita akan memanfaatkan teknik berupa *mock* dan *stub* untuk membuat kode cakupan setiap *test* kita menjadi lebih fokus terhadap objek yang benar-benar akan di-*test*.
+
+**Apa itu *mock*?**
+
+*Mock* adalah objek palsu yang kita bisa gunakan untuk menggunakan objek yang menjadi dependensi dari suatu fungsi/kelas.
+*Mock object* dapat kita gunakan untuk *tracking*, yaitu melihat bagaimana interaksi antara fungsi yang kita *test* dengan objek dependensi tersebut.
+
+Contoh sederhananya adalah, kita mau melihat apakah fungsi kita melakukan proses *Save Object* ke sebuah database.
+Berikut adalah contoh penggunaan *mock* di *test code*:
+```java
+    @InjectMocks
+    PaymentService service; // Mock object dari PaymentRepository akan ditanam dalam object "service"
+
+    @Mock
+    PaymentRepository repository; // Mock object dari PaymentRepository yang akan kita gunakan
+
+    // Test ini bertujuan untuk melihat apakah method "save" pada PaymentRepository dieksekusi setidaknya 1 kali ketika method "create" di PaymentService dieksekusi.
+    @Test
+    void testSaveObjectExecutedWhenCreateExecuted() {
+        service.create("a-01", "Bambang", 20000);
+        verify(repository, atLeastOnce()).save(any(Payment.class));
+    }
+```
+
+**Apa itu *stub*?**
+
+*Stub* adalah objek palsu yang kita bisa gunakan untuk menyimulasikan keluaran fungsi-fungsi pada objek tersebut.
+Hal ini sangat berguna jika kita menggunakan *library* eksternal atau API eksternal,
+sehingga *unit test* kita tidak akan membuang waktu dan *resource* untuk mengakses *library* atau API tersebut.
+Kita bisa meminta *stub* untuk mengembalikan hasil yang kita mau untuk diproses oleh fungsi yang kita *test*.
+Kita juga bisa meminta *stub* untuk memberikan *error* untuk menyimulasikan *negative case*.
+
+Contoh sederhananya adalah, kita mau menyimulasikan bahwa terdapat objek *Payment* di database,
+tanpa kita harus memasukkan entri *Payment* ke dalam database terlebih dahulu.
+Berikut adalah contoh penggunaan *stub* di *test code*:
+```java
+    @InjectMocks
+    PaymentService service; // Stub object dari PaymentRepository akan ditanam dalam object "service"
+
+    @Mock
+    PaymentRepository repository; // Stub object dari PaymentRepository yang akan kita gunakan
+
+    // Test ini bertujuan untuk melihat apakah "create" akan mengembalikan error jika payment dengan ID yang sama ("a-01") sudah ada.
+    @Test
+    void testCreatePaymentReturnsErrorIfPaymentAlreadyExists() {
+        // Proses stubbing: jika repository.getById("a-01") dieksekusi, kembalikan objek Payment yang sudah dibuat
+        Payment payment = new Payment("a-01", "Bambang", 20000);
+        when(repository.getById("a-01")).thenReturn(payment);
+
+        // Cek apakakh eksekusi method "create" akan memunculkan exception PaymentAlreadyCreated
+        assertThrows(PaymentAlreadyCreated.class, () -> {
+            service.create("a-01", "Usep", 100000);
+        });
+    }
+```
+
+
+## Persiapan Workshop
+
+Untuk mengikuti rangkaian kegiatan workshop hari ini, harap persiapkan _tools_ berikut di komputer anda:
+
+- Git
+- IntelliJ Community Edition
+- Java JDK 17 (`java` dan `javac`)
+- Apache Maven (`mvn`)
+
+Buat salinan _branch_ `main` dari repositori Git kode templat workshop hari ini:
+
+```shell
+git clone https://gitlab.cs.ui.ac.id/pmpl/workshops/sibayar.git
+```
+
+Apabila sudah menyalin repositori Git dan telah menyiapkan _tools_ yang dibutuhkan,
+Anda bisa menjalanakan aplikasi contoh dengan perintah *shell* berikut:
+
+```shell
+mvn package -DskipTests
+cp src/main/resources/application.properties target/application.properties
+cd target
+java -jar sibayar-0.1.1-SNAPSHOT.jar
+```
+
+Perlu diketahui bahwa aplikasi yang ada pada repo tersebut belum lengkap.
+Kita akan lengkapi bersama saat tutorial ini.
+
+## SIBAYAR: Pembayaran peer-to-peer dengan mekanisme Accept Payment dan Disbursement
+
+SIBAYAR merupakan sistem pembayaran peer-to-peer sederhana yang menggunakan sebuah API *payment gateway* (dalam kasus ini, Flip) untuk menyalurkan uang.
+Berikut adalah flow keseluruhan dari sistem SIBAYAR:
+1. User bisa melakukan transfer uang ke User lain, atau ke nomor rekening yang tidak terdaftar di SIBAYAR.
+2. SIBAYAR mengontak API Flip untuk mendapatkan link pembayaran,
+   yang bisa digunakan User untuk membayar transfer tersebut.
+3. User melakukan pembayaran menggunakan link pembayaran yang telah diberikan API Flip ke User melalui SIBAYAR.
+4. Jika user sudah selesai melakukan pembayaran,
+   API Flip akan mengeksekusi *payment callback endpoint* milik SIBAYAR untuk melanjutkan proses transfer.
+5. Ketika API Flip mengeksekusi *payment callback endpoint* SIBAYAR,
+   sistem SIBAYAR akan kembali mengontak API Flip untuk transfer uang (*disbursement*) ke rekening tujuan.
+6. Ketika Flip berhasil melakukan *disbursement*,
+   API Flip akan mengeksekusi *disbursement callback endpoint* milik SIBAYAR,
+   sehingga pembayaran tersebut akan ditandai **sukses** oleh SIBAYAR.
+
+Untuk tutorial hari ini, tidak perlu khawatirkan akses *callback* dari Flip,
+karena Flip hanya bisa melakukan *callback* ketika aplikasi SIBAYAR sudah di-*deploy* secara publik.
+Akan tetapi, kita tetap perlu mengimplementasikan keseluruhan sistem SIBAYAR, dengan menyusun tiga *controller*:
+1. `AuthenticationController`: untuk proses *login* dan *register* akun baru.
+2. `PaymentController`: untuk melakukan *payment* baru dan melihat histori *payment*.
+3. `CallbackController`: untuk *payment callback endpoint* dan *disbursement callback endpoint* yang akan dikontak oleh API Flip.
+
+## Membuat *Test Suite Class*
+
+Untuk proses pembuatan *unit test*, kita akan memanfaatkan library `Mockito`.
+`Mockito` berfungsi untuk membuat *mock* dan *stub* serta meng-*inject* objek-objek palsu tersebut (*dependency injection*) ke dalam objek yang akan kita *test*.
+Untuk menyusun *test suite* yang support `Mockito`,
+kita perlu tambahkan anotasi `@ExtendWith(MockitoExtension.class)`.
+
+Selain itu, kita perlu membuat fungsi yang akan dijalankan sebagai *set up* setiap *unit test* pada *test suite*.
+Fungsi `setUp()` akan dijalankan **sebelum** setiap *unit test* dijalankan.
+Dengan fungsi `setUp()`, kita bisa mengisolasi setiap *unit test* agar hasilnya bisa independen,
+selain itu juga meningkatkan *reusability* dan konsistensi antar *test case* karena menggunakan cara inisiasi yang serupa.
+
+Berikut adalah contoh inisiasi *test suite* untuk kelas `PaymentServiceImpl`.
+Buat sejajar dengan kelas aslinya, yaitu di *package* `com.example.sibayar.service.payment`:
+```java
+@ExtendWith(MockitoExtension.class)
+class PaymentServiceImplTest {
+
+    @InjectMocks
+    private PaymentServiceImpl service;
+    @Mock
+    private PaymentRepository paymentRepository;
+    @Mock
+    private UserRepository userRepository;
+    @Mock
+    User user;
+
+    Payment payment;
+    PaymentToUserRequest paymentToUserRequest;
+    PaymentToOtherRequest paymentToOtherRequest;
+    PaymentLinkResponse paymentLinkResponse;
+
+    // Instansiasi objek-objek yang akan menjadi hasil dari stub
+    @BeforeEach
+    void setUp() {
+        paymentToOtherRequest = PaymentToOtherRequest.builder()
+                                                     .destinationName("Pak Bambang")
+                                                     .destinationBankCode("bca")
+                                                     .destinationAccountNumber("123456789")
+                                                     .amount(50000)
+                                                     .build();
+        paymentToUserRequest = PaymentToUserRequest.builder()
+                                                   .destinationUserId(2)
+                                                   .amount(50000)
+                                                   .build();
+        paymentLinkResponse = PaymentLinkResponse.builder()
+                                                 .expiredDate(LocalDateTime.of(2023, 11, 4, 23, 59))
+                                                 .linkId(1)
+                                                 .linkUrl("https://flip.id/bambang")
+                                                 .build();
+        payment = Payment.builder()
+                         .id(1)
+                         .sender(user)
+                         .destinationName("Pak Bambang")
+                         .destinationBankCode("bca")
+                         .destinationAccountNumber("123456789")
+                         .amount(50000)
+                         .paymentLinkId(1)
+                         .paymentLinkUrl("https://flip.id/bambang")
+                         .expiredDate(LocalDateTime.of(2023, 11, 4, 23, 59))
+                         .status(String.valueOf(PaymentStatus.WAITING_PAYMENT))
+                         .build();
+    }
+}
+```
+
+Dalam kasus ini, terdapat 3 objek yang akan dibuat objek *mock*/*stub*-nya, yang ditandai dengan anotasi `@Mock`:
+1. `PaymentRepository`: repositori untuk Payment, diperlukan untuk operasi *database* terkait entri `Payment`.
+2. `UserRepository`L repositori untuk Payment, diperlukan untuk operasi *database* terkait entri `User`.
+3. `User`: objek data User (pengguna).
+
+Ketiga objek ini kemudian akan di-*inject* ke `PaymentServiceImpl` yang akan kita test, ditandai dengan anotasi `@InjectMocks`.
+
+Terdapapt juga 2 objek yang akan menjadi parameter dari *fungsi* yang akan di-*test*, yaitu:
+1. `PaymentToUserRequest`: *Data Transfer Object* (DTO) sebagai *parameter* fungsi `payToUser`, yang datang dari `PaymentController`.
+2. `PaymentToOtherRequest`: *Data Transfer Object* (DTO) sebagai *parameter* fungsi `payToOtherDestination`, yang datang dari `PaymentController`.
+
+Terdapat juga 2 objek yang akan menjadi hasil dari *stub*, yaitu:
+1. `Payment`: objek data Payment.
+2. `PaymentLinkResponse`: *Data Transfer Object* (DTO) untuk menampung hasil kembalian dari Flip API ketika membuat *payment link*.
+
+### Tugas Anda
+- [ ] Silakan *copy* snippet kode yang telah dijelaskan ke dalam project Anda.
+
+### Latihan Mandiri: Inisiasi *test suite* `CallbackServiceImpl`
+- [ ] Buat *test suite* `CallbackServiceImpl`, sejajar dengan *test suite* `PaymentServiceImpl` yang telah dibuat.
+- [ ] *Test suite* terdiri dari objek *mock/stub* berikut:
+  1. `PaymentRepositoryImpl`
+- [ ] *Test suite* terdiri dari objek untuk parameter fungsi yang di-*test* berikut:
+  1. `PaymentCallbackRequest`
+  2. `DisbursementCallbackRequest`
+- [ ] *Test suite* terdiri dari objek *hasil stub* berikut:
+  1. `DisbursementResponse`
+- [ ] Buat fungsi `setUp()` untuk menyusun objek-objek *hasil stub*.
+
+
+## Melakukan *Mock* dan *Stub* untuk Akses Database dan Helper Function Lain
+
+SIBAYAR menggunakan Spring Data JPA untuk mengakases database In-Memory H2.
+Dalam menggunakan Spring Data JPA, kita perlu membuat *repository interface* yang berisikan daftar *method* untuk mengakses *database*.
+JPA kemudian akan membuatkan implementasi setiap *method* secara *on the fly* sesuai dengan nama *method* yang kita gunakan.
+*Repository interface* tersebut akan menjadi sebuah komponen Spring yang bisa digunakan untuk *service* yang akan kita buat bersama.
+Di tutorial hari ini, sudah tersedia *repository* untuk objek `Payment` dan objek `User`.
+
+*Database* adalah komponen dependensi pada objek *service* yang perlu kita isolasi.
+*Programmer* pada umumnya akan membuat entri ke *database* lalu akan menghapusnya kembali ketika test selesai.
+Akan tetapi, hal tersebut jadi memakan *resource* lebih dengan perlu adanya *testing database* yang nyata.
+Oleh karena itu, dalam menyusun *unit test* untuk *service*, kita perlu melakukan *mock* dan *stub* komponen *Repository*.
+Sebagai contoh, berikut adalah beberapa *test case* yang bisa Anda gunakan:
+
+### `payToUser` sukses menyimpan dan mengembalikan objek `Payment` baru
+```java
+    @Test
+    void testPayToUserSuccess() {
+        when(userRepository.findById(any(Integer.class))).thenReturn(Optional.of(user));
+        when(service.getPaymentLink(any(PaymentLinkRequest.class))).thenReturn(paymentLinkResponse);
+
+        Payment result = service.payToUser(1, paymentToUserRequest);
+
+        verify(service, atLeastOnce()).getPaymentLink(any(PaymentLinkRequest.class));
+        verify(paymentRepository, atLeastOnce()).save(result);
+
+        assertEquals(paymentLinkResponse.getExpiredDate(), result.getExpiredDate());
+        assertEquals(paymentLinkResponse.getLinkId(), result.getPaymentLinkId());
+        assertEquals(paymentLinkResponse.getLinkUrl(), result.getPaymentLink());
+    }
+```
+
+Terlihat bahwa terdapat *stubbing* untuk fungsi `userRepository.findById(Integer)` untuk menyimulasikan bahwa di database terdapat objek *mock* `user`.
+Selain itu, juga terdapat *stubbing* untuk fungsi `service.getPaymentLink(PaymentLinkRequest)` untuk mengembalikan objek `paymentLinkResponse`.
+*Test case* ini akan mengecek:
+1. Apakah fungsi `payToUser` memanggil *helper function* `getPaymentLink` untuk akses API Flip?
+2. Apakah fungsi `payToUser` memanggil fungsi `save` pada `PaymentRepository` untuk menyimpan objek `Payment` ke dalam database?
+3. Apakah fungsi `payToUser` mengembalikan objek `Payment`, dengan isi yang sama seperti yang dikembalikan oleh API Flip?
+
+### `paymentCallback` sukses menyimpan status `WAITING_DISBURSEMENT` ketika status Payment Link `"SUCCESSFUL"` dan status Disbursement `"PENDING"`
+```java
+    @Test
+    void testPaymentCallbackWithSuccessfulStatusAndPendingDisbursement() {
+        when(paymentRepository.findByPaymentLinkId(any(Integer.class))).thenReturn(Optional.of(payment));
+        paymentCallbackRequest.setStatus("SUCCESSFUL");
+        when(service.disburseMoney(any(DisbursementRequest.class))).thenReturn(disbursementResponse);
+        disbursementResponse.setStatus("PENDING");
+
+        Payment payment = service.paymentCallback(paymentCallbackRequest);
+
+        verify(service, atLeastOnce()).disburseMoney(any(DisbursementRequest.class));
+        verify(paymentRepository, atLeastOnce()).save(result);
+
+        assertEquals(PaymentStatus.WAITING_DISBURSEMENT.getStatus(), payment.getStatus());
+    }
+```
+
+Fungsi `paymentCallback` bertujuan untuk meneruskan pembayaran lewat mekanisme *Disbursement* jika status Payment Link yang diberikan `"SUCCESSFUL"`.
+
+Terlihat bahwa terdapat *stubbing* untuk fungsi `paymentRepository.findByPaymentLinkId(Integer)` untuk menyimulasikan bahwa di database terdapat objek `payment`.
+Selain itu, juga terdapat *stubbing* untuk fungsi `service.disburseMoney(DisbursementRequest)` untuk mengembalikan objek `paymentLinkResponse`.
+Selain itu, kita juga perlu *set status* pada objek `paymentCallbackRequest` menjadi `"SUCCESSFUL"`.
+Selain itu, kita juga perlu *set status* pada objek `disbursementResponse` menjadi `"PENDING"` untuk menyimulasikan API Flip sedang melakukan proses *disbursement*.
+*Test case* ini akan mengecek:
+1. Apakah fungsi `paymentCallback` memanggil *helper function* `disburseMoney` untuk akses API Flip?
+2. Apakah fungsi `paymentCallback` memanggil fungsi `save` pada `PaymentRepository` untuk menyimpan perubahan objek `Payment` ke dalam database?
+3. Apakah fungsi `paymentCallback` mengembalikan objek `Payment` dengan `status` berupa `"WAITING_DISBURSEMENT"`?
+
+### `paymentCallback` sukses menyimpan status `WAITING_DISBURSEMENT` ketika status Payment Link `"SUCCESSFUL"` dan status Disbursement `"DONE"`
+```java
+    @Test
+    void testPaymentCallbackWithSuccessfulStatusAndSuccessfulDisbursement() {
+        when(paymentRepository.findByPaymentLinkId(any(Integer.class))).thenReturn(Optional.of(payment));
+        paymentCallbackRequest.setStatus("SUCCESSFUL");
+        when(service.disburseMoney(any(DisbursementRequest.class))).thenReturn(disbursementResponse);
+        disbursementResponse.setStatus("DONE");
+
+        Payment payment = service.paymentCallback(paymentCallbackRequest);
+
+        verify(service, atLeastOnce()).disburseMoney(any(DisbursementRequest.class));
+        verify(paymentRepository, atLeastOnce()).save(result);
+
+        assertEquals(PaymentStatus.SUCCESS.getStatus(), payment.getStatus());
+    }
+```
+
+Terlihat bahwa terdapat *stubbing* untuk fungsi `paymentRepository.findByPaymentLinkId(Integer)` untuk menyimulasikan bahwa di database terdapat objek `payment`.
+Selain itu, juga terdapat *stubbing* untuk fungsi `service.disburseMoney(DisbursementRequest)` untuk mengembalikan objek `paymentLinkResponse`.
+Selain itu, kita juga perlu *set status* pada objek `paymentCallbackRequest` menjadi `"SUCCESSFUL"`.
+Selain itu, kita juga perlu *set status* pada objek `disbursementResponse` menjadi `"DONE"` untuk menyimulasikan API Flip langsung selesai melakukan *disbursement*.
+*Test case* ini akan mengecek:
+1. Apakah fungsi `paymentCallback` memanggil *helper function* `disburseMoney` untuk akses API Flip?
+2. Apakah fungsi `paymentCallback` memanggil fungsi `save` pada `PaymentRepository` untuk menyimpan perubahan objek `Payment` ke dalam database?
+3. Apakah fungsi `paymentCallback` mengembalikan objek `Payment` dengan `status` berupa `"SUCCESS"`?
+
+### Tugas Anda
+- [ ] Silakan *copy* semua snippet kode yang telah dijelaskan ke dalam project Anda ke tempat yang sesuai.
+  - `testPayToUserSuccess` diletakkan di `PaymentServiceImplTest`.
+  - `testPaymentCallbackWithSuccessfulStatusAndPendingDisbursement` diletakkan di `CallbackServiceImplTest`.
+  - `testPaymentCallbackWithSuccessfulStatusAndSuccessfulDisbursement` diletakkan di `CallbackServiceImplTest`.
+
+### Latihan Mandiri: Buat *positive case*
+- [ ] Buat *test case* `testPayToOtherSuccess` pada *test suite* `PaymentServiceImplTest` untuk mengecek:
+  1. Apakah fungsi `payToOtherDestination` memanggil *helper function* `getPaymentLink` untuk akses API Flip?
+  2. Apakah fungsi `payToOtherDestination` memanggil fungsi `save` pada `PaymentRepository` untuk menyimpan objek `Payment` ke dalam database?
+  3. Apakah fungsi `payToOtherDestination` mengembalikan objek `Payment`, dengan isi yang sama seperti yang dikembalikan oleh API Flip?
+
+### Latihan Mandiri: Buat *negative case*
+- [ ] Buat *test case* `testPaymentCallbackWithSuccessfulStatusButCancelledDisbursement` pada *test suite* `CallbackServiceImplTest` untuk mengecek:
+  1. Apakah fungsi `paymentCallback` memanggil *helper function* `getPaymentLink` untuk akses API Flip?
+  2. Apakah fungsi `paymentCallback` memanggil fungsi `save` pada `PaymentRepository` untuk menyimpan perubahan objek `Payment` ke dalam database?
+  3. Apakah fungsi `paymentCallback` mengembalikan objek `Payment` dengan `status` berupa `"DISBURSEMENT_FAILED"`?
+  - **CATATAN**: `status` pada `disbursementResponse` harus diganti dengan `"CANCELLED"`.
+- [ ] Buat *test case* `testPaymentCallbackWithCancelledStatus` pada *test suite* `CallbackServiceImplTest` untuk mengecek:
+  1. Apakah fungsi `paymentCallback` **TIDAK** memanggil *helper function* `getPaymentLink` untuk akses API Flip?<br />
+      **HINT**: gunakan
+      ```java
+      verify(service, atMost(0)).disburseMoney(any(DisbursementRequest.class))
+      ```
+      untuk mengecek bahwa *helper function* tidak dipanggil.
+  2. Apakah fungsi `paymentCallback` memanggil fungsi `save` pada `PaymentRepository` untuk menyimpan perubahan objek `Payment` ke dalam database?
+  3. Apakah fungsi `paymentCallback` mengembalikan objek `Payment` dengan `status` berupa `"PAYMENT_FAILED"`?
+  - **CATATAN**: `status` pada `paymentCallbackRequest` harus diganti dengan `"CANCELLED"`.
+
+
+## Menguji Apakah Fungsi Mengeluarkan *Exception* dalam Suatu *Test Case*
+
+*Unit test* tidak hanya mencakup *positive case* (dalam kasus ini: mengembalikan objek `Payment`),
+akan tetapi juga perlu mencakup *negative case* (dalam kasus ini: mengeluarkan sebuah *exception*).
+Sebagai contoh, berikut adalah beberapa *negative case* yang bisa Anda gunakan:
+
+### `payToUser` pada `PaymentServiceImpl` akan mengembalikan *error* ketika user ID dari *sender* dan *destination* sama.
+```java
+    @Test
+    void testPayToUserFailWhenSenderAndDestinationAreTheSame() {
+        assertThrows(SelfPaymentException.class, () -> {
+            service.payToUser(paymentToUserRequest.getDestinationUserId(), paymentToUserRequest);
+        });
+    }
+```
+
+*Test case* ini akan mengecek apakah *exception* `SelfPaymentException` dikeluarkan jika user ID dari *sender* dan *destination* sama.
+
+### `payToUser` pada `PaymentServiceImpl` akan mengembalikan *error* jika Flip API sedang tidak bisa diakses.
+```java
+    @Test
+    void testPayToUserThrowsExceptionWhenAPIIsUnreachable() {
+        when(userRepository.findById(any(Integer.class))).thenReturn(Optional.of(user));
+        when(service.getPaymentLink(any(PaymentLinkRequest.class))).thenThrow(new APIUnreachableException("flip"));
+
+        assertThrows(APIUnreachableException.class, () -> {
+            service.payToUser(1, paymentToUserRequest);
+        });
+        verify(service, atLeastOnce()).getPaymentLink(any(PaymentLinkRequest.class));
+    }
+```
+
+Terlihat bahwa terdapat *stubbing* untuk fungsi `userRepository.findById(Integer)` untuk mengembalikan objek *mock* `user`.
+Selain itu, juga terdapat *stubbing* untuk fungsi `service.getPaymentLink(PaymentLinkRequest)` untuk melempar *exception* `APIUnreachableException`.
+*Test case* ini akan mengecek:
+1. Apakah fungsi `payToUser` meneruskan *exception* `APIUnreachableException`?
+2. Apakah fungsi `payToUser` memanggil *helper function* `getPaymentLink` untuk akses API Flip?
+
+### `paymentCallback` pada `CallbackServiceImpl` akan mengembalikan *error* jika pembayaran lewat payment link sukses, namun Flip API untuk *disbursement* sedang tidak bisa diakses.
+```java
+    @Test
+    void testPaymentCallbackWithSuccessfulStatusThrowsExceptionWhenAPIIsUnreachable() {
+        when(paymentRepository.findByPaymentLinkId(any(Integer.class))).thenReturn(Optional.of(payment));
+        paymentCallbackRequest.setStatus("SUCCESSFUL");
+        when(service.disburseMoney(any(DisbursementRequest.class))).thenThrow(new APIUnreachableException("flip"));
+
+        assertThrows(APIUnreachableException.class, () -> {
+            service.paymentCallback(paymentCallbackRequest);
+        });
+        verify(service, atLeastOnce()).disburseMoney(any(DisbursementRequest.class));
+    }
+```
+
+Terlihat bahwa terdapat *stubbing* untuk fungsi `paymentRepository.findByPaymentLinkId(Integer)` untuk mengembalikan objek `payment`.
+Selain itu, juga terdapat *stubbing* untuk fungsi `service.disburseMoney(DisbursementRequest)` untuk melempar *exception* `APIUnreachableException`.
+Selain itu, kita juga perlu *set status* pada objek `paymentCallbackRequest` menjadi `"SUCCESSFUL"`,
+karena akses API Flip untuk *disbursement* hanya akan diakses jika status *payment link* sudah `SUCCESSFUL`.
+*Test case* ini akan mengecek:
+1. Apakah fungsi `payToUser` meneruskan *exception* `APIUnreachableException`?
+2. Apakah fungsi `payToUser` memanggil *helper function* `disburseMoney` untuk akses API Flip?
+
+### Tugas Anda
+- [ ] Silakan *copy* semua snippet kode yang telah dijelaskan ke dalam project Anda ke tempat yang sesuai.
+  - `testPayToUserFailWhenSenderAndDestinationAreTheSame` diletakkan di `PaymentServiceImplTest`.
+  - `testPayToUserThrowsExceptionWhenAPIIsUnreachable` diletakkan di `PaymentServiceImplTest`.
+  - `testPaymentCallbackWithSuccessfulStatusThrowsExceptionWhenAPIIsUnreachable` diletakkan di `CallbackServiceImplTest`.
+
+
+### Latihan Mandiri: Buat *negative case*
+- [ ] Buat *test case* `testPayToOtherThrowsExceptionWhenAPIIsUnreachable` pada *test suite* `PaymentServiceImplTest` untuk mengecek:
+  1. Apakah fungsi `payToOtherDestination` meneruskan *exception* `APIUnreachableException`?
+  2. Apakah fungsi `payToOtherDestination` menjalankan *helper function* `getPaymentLink` untuk akses API Flip?
+
+
+## *Refactoring*: Bagaimana jika SIBAYAR bisa menggunakan lebih dari satu Payment Gateway?
+
+Ternyata kode SIBAYAR yang asli belum cukup *modular*.
+Bagaimana jika suatu hari kita tidak menggunakan Flip sebagai *payment gateway*?
+Salah satu cara yang dapat kita lakukan adalah memisahkan *helper function* `getPaymentLink` dan `disburseMoney` menjadi sebuah *class* tersendiri di bawah package `com.example.sibayar.external.paymentgateway`.
+Lalu, bagaimana cara kita menyesuaikan implementasi dan *unit test* kita ketika ada perubahan desain tersebut?
+
+### Tugas Anda
+- [ ] Buat interface baru `PaymentGatewayAPI`, dengan menggunakan *snippet* berikut:
+  ```java
+  public interface PaymentGatewayAPI {
+      PaymentLinkResponse getPaymentLink(PaymentLinkRequest request);
+      DisbursementResponse disburseMoney(DisbursementRequest request);
+  }
+  ```
+- [ ] Buat class baru `FlipAPI`, dengan menggunakan *snippet* berikut:
+  ```java
+  @Service("flipAPI")
+  @RequiredArgsConstructor
+  public class FlipAPI implements PaymentGatewayAPI {
+      @Value("${sibayar.flip.baseUrl}")
+      private String baseUrl;
+      @Value("${sibayar.flip.apiKey}")
+      private String apiKey;
+      private HttpClient client = HttpClient.newHttpClient();
+
+      public PaymentLinkResponse getPaymentLink(PaymentLinkRequest request) {
+          // TODO: Pindahkan isi fungsi getPaymentLink di PaymentServiceImpl ke sini.
+      }
+
+      public DisbursementResponse disburseMoney(DisbursementRequest request) {
+          // TODO: Pindahkan isi fungsi disburseMoney di CallbackServiceImpl ke sini.
+      }
+
+      private String getBasicAuthHeader(String username, String password) {
+          String valueToEncode = username + ":" + password;
+          return "Basic " + Base64.getEncoder()
+                                  .encodeToString(valueToEncode.getBytes());
+      }
+  }
+  ```
+- [ ] Pada `PaymentServiceImpl` dan `CallbackServiceImpl`, buat koneksi baru ke `PaymentGatewayAPI` dengan menggunakan *snippet* berikut:
+  ```java
+      @Qualifier("flipAPI")
+      private final PaymentGatewayAPI api;
+  ```
+- [ ] Gunakan `api` untuk mengakses fungsi `getPaymentLink` dan `disburseMoney` yang telah dipindahkan.
+- [ ] Tambahkan objek *mock* untuk `PaymentGatewayAPI` pada *test suite* `PaymentServiceImplTest` dan `CallbackServiceImplTest` dengan menggunakan *snippet* berikut:
+  ```java
+      @Mock
+      private PaymentGatewayAPI flipAPI;
+  ```
+- [ ] Gunakan objek *mock* `flipAPI` untuk menggantikan pemanggilan fungsi `getPaymentLink` dan `disburseMoney` pada setiap `test case`. Misal:
+  ```java
+      when(service.disburseMoney(any(DisbursementRequest.class))).thenReturn(disbursementResponse);
+      // ...
+      verify(service, atLeastOnce()).disburseMoney(any(DisbursementRequest.class));
+  ```
+  menjadi seperti berikut:
+  ```java
+      when(flipAPI.disburseMoney(any(DisbursementRequest.class))).thenReturn(disbursementResponse);
+      // ...
+      verify(flipAPI, atLeastOnce()).disburseMoney(any(DisbursementRequest.class));
+  ```
+
+## Latihan Mandiri Tambahan:
+- [ ] Lengkapi semua *test case* hingga Line Coverage menyentuh 100%.
+- [ ] Buat kode *test case* yang *meaningful* dan *thorough*.
+
+## Penutup
+Kita sudah bersama-sama membuat *unit test* untuk Service, lengkap dengan cara  menggunakan *mock* dan *stub*.
+
+Untuk bahan diskusi saat refleksi:
+- [ ] Apakah Line Coverage 100% menjamin tidak ada bug?
+- [ ] Bagaimana jika kita langsung melakukan modifikasi *database* atau mengakses langsung *library* atau API eksternal saat kita melakukan *unit test*? Apa dampaknya bagi konsistensi hasil dari *unit test*?
+- [ ] Apa kesulitan yang dialami Bapak/Ibu ketika menjalani tutorial ini?
+
+
+
+[1]: https://docs.google.com/presentation/d/1f1vpyYOu3GSyIeyqGLU-D6REBn0ACN81?rtpof=true&usp=drive_fs