Enabling JoinAcross to merge non-joined keys

That's ugly but works:

JoinAcrossMerge[
  datasets : {__Dataset}, across : {__String}, butAlso : {__String}, by_: Identity
] := GroupBy[
     Join @@ datasets,
     Lookup[across] -> KeyTake[Join[across, butAlso]],
     If[Length[#] == 1, <||>, <|#[[1]][[across]], Merge[#[[;; , butAlso]], by]|>] &
][Values@*DeleteCases[<||>]] // Dataset



JoinAcrossMerge[ {d1, d2}, {"f1", "f2"}, {"v1", "v2"}, foo ]

enter image description here


Partial solution using nested Merge followed by a Join across key-value pairs:

Dataset[{d1, d2}][All, Normal][All, GroupBy[Query[{"f1", "f2"}]], 
  All, {"v1", "v2"}][
 Merge[Merge[Identity]] /* Select[Length[#v1] == 2 &] /*  
  KeyValueMap[Join]]

enter image description here

This is less than Kuba's solution. I don't know how to avoid the Select, which would break if foo is substituted for Identity. However fits OPs requirements.

How it works

This functional pattern leverages WL's exceptional key-value semantics where Keys can be general expressions as seen by this query fragment (red frame outlines desired rows):

Dataset[{d1, d2}][All, Normal][All, GroupBy[Query[{"f1", "f2"}]], 
  All, {"v1", "v2"}][Merge[Merge[Identity]]]  

enter image description here